<template>
  <div ref="verify" :style="style" class="simple-verify">
    <div class="verify-tips">{{ tips }}</div>
    <div :style="slideBoxStyle" class="verify-box">
      <div ref="slide" class="veriry-slide" :style="slideStyle" />
    </div>
    <div ref="bar" class="verify-bar">
      <div :style="barStyle" class="icon" />
   </div>
    <div ref="text" class="verify-success-tips">
      <span :style="iconStyle" />{{ successTips }}
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue'
import barImage from './images/bar'
import successImage from './images/success'

// Props
const props = defineProps({
  /** 长度 */
  width: {
    type: Number,
    default: 330
  },
  /** 宽度 */
  height: {
    type: Number,
    default: 36
  },
  /** 边框颜色 */
  borderColor: {
    type: String,
    default: '#E4E4E4'
  },
  /** 背景颜色 */
  bgColor: {
    type: String,
    default: '#F2F3F5'
  },
  /** 圆角大小 */
  borderRadius: {
    type: Number,
    default: 4
  },
  /** 提示文案 */
  tips: {
    type: String,
    default: '请按住滑块，拖动到最右边'
  },
  /** 滑块的背景颜色，可以是图片 */
  barBackground: {
    type: String,
    default: `url(${barImage})`
  },
  /** 滑过颜色 */
  movedColor: {
    type: String,
    default: 'linear-gradient(313deg, rgba(65, 209, 102, 1) 0%, rgba(90, 232, 118, 1) 100%)'
  },
  /** 成功文案 */
  successTips: {
    type: String,
    default: '完成验证'
  },
  /** 成功后的图标 */
  successIcon: {
    type: String,
    default: successImage
  }
})

// Emits
const emit = defineEmits(['success', 'reset'])

// Refs
const verify = ref<HTMLElement | null>(null)
const bar = ref<HTMLElement | null>(null)
const text = ref<HTMLElement | null>(null)
const slide = ref<HTMLElement | null>(null)

// State
const x1 = ref(0)
const x2 = ref(0)
const isMousedown = ref(false)
const isSuccess = ref(false)

// Computed
const max = computed(() => props.width - 50)
const style = computed(() => ({
  width: `${props.width}px`,
  height: `${props.height}px`,
  border: `${props.borderColor} 1px solid`,
  backgroundColor: props.bgColor,
  borderRadius: `${props.borderRadius}px`
}))
const barStyle = computed(() => ({
  background: props.barBackground
}))
const slideBoxStyle = computed(() => ({
  borderRadius: `${props.borderRadius}px`
}))
const slideStyle = computed(() => ({
  borderRadius: `${props.borderRadius}px`,
  background: props.movedColor,
  left: `${50 - props.width}px`
}))
const iconStyle = computed(() => ({
  background: `url(${props.successIcon}) no-repeat`
}))

// Methods
const mouseenter = (e: MouseEvent | TouchEvent) => {
  if (isSuccess.value) return
  if (slide.value) {
    slide.value.style.opacity = '1'
  }
}

const mouseleave = (e: MouseEvent | TouchEvent) => {
  if (isSuccess.value || isMousedown.value) return
  if (slide.value) {
    slide.value.style.opacity = '0'
    const transitionDuration = '.3s'
    const transform = 'translateX(0)'
    setStyle(transitionDuration, transform)
  }
}

const mousedown = (e: MouseEvent | TouchEvent) => {
  if (isSuccess.value) return
  const clientX = (e as MouseEvent).x || (e as TouchEvent).targetTouches[0].clientX
  x1.value = clientX
  isMousedown.value = true
}

const mousemove = (e: MouseEvent | TouchEvent) => {
  if (!isMousedown.value || isSuccess.value) return
  e.preventDefault()
  e.stopPropagation()
  const clientX = (e as MouseEvent).x || (e as TouchEvent).targetTouches[0].clientX
  x2.value = clientX
  let diff = x2.value - x1.value
  if (diff < 0) diff = 0
  if (diff >= max.value) {
    diff = max.value
    isSuccess.value = true
    if (text.value) text.value.style.opacity = '1'
    emit('success')
  }
  const transitionDuration = '0s'
  const transform = `translateX(${diff}px)`
  setStyle(transitionDuration, transform)
}

const mouseup = () => {
  if (isSuccess.value) return
  isMousedown.value = false
  const transitionDuration = '.3s'
  const transform = 'translateX(0)'
  setStyle(transitionDuration, transform)
}

const setStyle = (transitionDuration: string, transform: string) => {
  if (slide.value && bar.value) {
    // 滑块
    slide.value.style.transitionDuration = transitionDuration
    slide.value.style.webkitTransitionDuration = transitionDuration
    slide.value.style.transform = transform
    slide.value.style.webkitTransform = transform
    // 按钮
    bar.value.style.transitionDuration = transitionDuration
    bar.value.style.webkitTransitionDuration = transitionDuration
    bar.value.style.transform = transform
    bar.value.style.webkitTransform = transform
  }
}

const reset = () => {
  isSuccess.value = false
  isMousedown.value = false
  const transitionDuration = '0s'
  const transform = 'translateX(0)'
  setStyle(transitionDuration, transform)
  if (text.value) text.value.style.opacity = '0'
  if (slide.value) slide.value.style.opacity = '0'
}

// 暴露方法给父组件
defineExpose({
  reset
})

// Lifecycle
onMounted(() => {
  if (bar.value && verify.value) {
    bar.value.addEventListener('mouseenter', mouseenter)
    bar.value.addEventListener('touchstart', mouseenter)
    bar.value.addEventListener('mouseleave', mouseleave)
    bar.value.addEventListener('touchend', mouseleave)
    bar.value.addEventListener('mousedown', mousedown)
    bar.value.addEventListener('touchstart', mousedown)
    document.body.addEventListener('mousemove', mousemove, { passive: false })
    document.body.addEventListener('touchmove', mousemove, { passive: false })
    document.body.addEventListener('mouseup', mouseup)
    document.body.addEventListener('touchend', mouseup)
    verify.value.addEventListener('reset', reset)
  }
})

onUnmounted(() => {
  document.body.removeEventListener('mousemove', mousemove)
  document.body.removeEventListener('touchmove', mousemove)
  document.body.removeEventListener('mouseup', mouseup)
  document.body.removeEventListener('touchend', mouseup)
})
</script>

<style lang="scss">
@use "./css/VueSimpleVerify.scss";
</style>